/*
 * This file is part of the Linux NIC driver for Emulex networking products.
 *
 * Copyright (C) 2005-2012 Emulex. All rights reserved.
 *
 * EMULEX and SLI are trademarks of Emulex.
 * www.emulex.com
 * linux-drivers@emulex.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful.
 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO THE
 * EXTENT THAT SUCH DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
 * See the GNU General Public License for more details, a copy of which
 * can be found in the file COPYING included with this package
 */

#include "be.h"

#ifndef GET_NUM_DEF_RSS_defined
/**
 * netif_get_num_default_rss_queues - default number of RSS queues
 *
 * This routine should set an upper limit on the number of RSS queues
 * used by default by multiqueue devices.
 */
int netif_get_num_default_rss_queues(void)
{
	return DEFAULT_MAX_NUM_RSS_QUEUES;
}
#endif /* GET_NUM_DEF_RSS_defined */

#ifndef PCI_SRIOV_GET_TOTALVFS_defined
int pci_sriov_get_totalvfs(struct pci_dev *pdev)
{
	u16 num = 0;
	int pos;

	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
	if (pos)
		pci_read_config_word(pdev, pos + PCI_SRIOV_TOTAL_VF, &num);
	return num;
}
#endif /* PCI_SRIOV_GET_TOTALVFS_defined */

void be_update_xmit_trans_start(struct net_device *netdev, int i)
{
#ifdef TXQ_TRANS_UPDATE_defined
	if (netdev->features & NETIF_F_LLTX)
		netdev_get_tx_queue(netdev, i)->trans_start = jiffies;
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
	netdev->trans_start = jiffies;
#endif
}

#ifdef CONFIG_PCI_IOV
int be_find_vfs(struct pci_dev *pdev, int vf_state)
{
	struct pci_dev *dev = pdev;
	int vfs = 0, assigned_vfs = 0, pos;

	if (!sriov_kernel)
		return 0;

	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
	if (!pos)
		return 0;

	dev = pci_get_device(pdev->vendor, PCI_ANY_ID, NULL);
	while (dev) {
		if (dev->is_virtfn && pci_physfn(dev) == pdev) {
			vfs++;
			if (dev->dev_flags & PCI_DEV_FLAGS_ASSIGNED)
				assigned_vfs++;
		}
		dev = pci_get_device(pdev->vendor, PCI_ANY_ID, dev);
	}
	return (vf_state == ASSIGNED) ? assigned_vfs : vfs;
}

#ifndef PCI_VFS_ASSIGNED_defined
/**
 * pci_vfs_assigned - returns number of VFs are assigned to a guest
 * @dev: the PCI device
 *
 * Returns number of VFs belonging to this device that are assigned to a guest.
 * If device is not a physical function returns -ENODEV.
 */
int pci_vfs_assigned(struct pci_dev *pdev)
{
	return be_find_vfs(pdev, ASSIGNED);
}
#endif /* PCI_VFS_ASSIGNED_defined */
#ifndef PCI_NUM_VF_defined
/**
 * pci_num_vf - return number of VFs associated with a PF device_release_driver
 * @dev: the PCI device
 *
 * Returns number of VFs, or 0 if SR-IOV is not enabled.
 */
int pci_num_vf(struct pci_dev *pdev)
{
	return be_find_vfs(pdev, ENABLED);
}
#endif /* PCI_NUM_VF_defined */
#endif /* CONFIG_PCI_IOV */

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
void be_netdev_ops_init(struct net_device *netdev, struct net_device_ops *ops)
{
	netdev->open = ops->ndo_open;
	netdev->stop = ops->ndo_stop;
	netdev->hard_start_xmit = ops->ndo_start_xmit;
	netdev->set_mac_address = ops->ndo_set_mac_address;
	netdev->get_stats = ops->ndo_get_stats;
	netdev->set_multicast_list = ops->ndo_set_rx_mode;
	netdev->change_mtu = ops->ndo_change_mtu;
	netdev->vlan_rx_register = ops->ndo_vlan_rx_register;
	netdev->vlan_rx_add_vid = ops->ndo_vlan_rx_add_vid;
	netdev->vlan_rx_kill_vid = ops->ndo_vlan_rx_kill_vid;
	netdev->do_ioctl = ops->ndo_do_ioctl;
#ifdef CONFIG_NET_POLL_CONTROLLER
	netdev->poll_controller = ops->ndo_poll_controller;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
	netdev->select_queue = ops->ndo_select_queue;
#endif
}
#endif

/* New NAPI backport */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)

int be_poll_compat(struct net_device *netdev, int *budget)
{
	struct napi_struct *napi = netdev->priv;
	u32 work_done, can_do;

	can_do = min(*budget, netdev->quota);
	work_done = napi->poll(napi, can_do);

	*budget -= work_done;
	netdev->quota -= work_done;
	return (work_done >= can_do);
}

void netif_napi_add_compat(struct net_device *netdev,
		struct napi_struct *napi,
		int (*poll) (struct napi_struct *, int), int weight)
{
	struct net_device *nd;

	nd = alloc_netdev(0, "", ether_setup);
	if (!nd)
		return;
	nd->priv = napi;
	nd->weight = BE_NAPI_WEIGHT;
	nd->poll = be_poll_compat;
	set_bit(__LINK_STATE_START, &nd->state);
	napi->poll = poll;
	napi->dev = nd;
#ifdef RHEL
	napi->napi.dev = netdev;
#endif
}

void netif_napi_del_compat(struct napi_struct *napi)
{
	if (napi->dev) {
		free_netdev(napi->dev);
		napi->dev = NULL;
	}
}
#endif /* New NAPI backport */
